﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using T.ServiceComponent.Kernel.Service;
using T.ServiceComponent.Log.SPI;

namespace T.FAS.Runtime.Action.SPI
{
    /// <summary>
    /// BPM执行日志服务
    /// </summary>
    internal class LogClient
    {
        private Task logSendTask;
        private bool isExist = false;
        private AutoResetEvent autoResetLog;
        private static string siteName = null;
        private static object lckObj = new object();
        private static Dictionary<string, string> tags = null;
        private static ConcurrentBag<ILog> sysActionLogs = null;
        private static ConcurrentBag<ILog> exceptionLogs = null;
        private static ConcurrentBag<ILog> trackLogs = null;

        private static LogClient current;

        public static LogClient Current
        {
            get
            {
                if (current == null)
                {
                    lock (lckObj)
                    {
                        if (current == null)
                            current = new LogClient();
                    }
                }

                return current;
            }
        }

        private LogClient()
        {
            sysActionLogs = new ConcurrentBag<ILog>();
            exceptionLogs = new ConcurrentBag<ILog>();
            trackLogs = new ConcurrentBag<ILog>();

            autoResetLog = new AutoResetEvent(false);
            tags = new Dictionary<string, string>();

            tags.Add("Host", GetHostName());
            tags.Add("Type", "WorkFlow");

            StartLogProcess();
        }

        private static string hostName = string.Empty;

        /// <summary>
        /// 主机名
        /// </summary>
        /// <returns>主机名</returns>
        private string GetHostName()
        {
            if (string.IsNullOrEmpty(hostName))
                hostName = Dns.GetHostName();

            return hostName;
        }
        /// <summary>
        /// 系统运行日志
        /// </summary>
        /// <param name="metaDataID"></param>
        /// <param name="dataID"></param>
        /// <param name="userID"></param>
        /// <param name="action"></param>
        /// <param name="isSuccess"></param>
        public void LogSysAction(string metaDataID ,string dataID,string userID, string action,bool isSuccess)
        {
            string msgId = RpcContext.Current.RequestID;
            var log = new SysActionLog()
            {
                Action = action == null ? "ActionRTP" : action,
                AppCode = "FAS",
                CreateTime = DateTime.Now,

                Result = isSuccess ? "成功" : "失败",
                ModuleCode = "ActionRTP",
                UserID = userID,
                ServerIP = GetHostName(),
                Ext1 = metaDataID,
                Ext2 = dataID,
                Ext3 = msgId,
                ExtColumnNames = $"Ext1=元数据ID,Ext2=数据ID,Ext3=全链路Id"
            };

            CacheLog(log);
        }
        /// <summary>
        /// 异常日志
        /// </summary>
        /// <param name="content"></param>
        /// <param name="exceptioCode"></param>
        /// <param name="ex"></param>
        public void LogError(string content = null, string exceptioCode = null, Exception ex = null)
        {
            string exMsg = ex != null ? ex.ToString() : string.Empty;
            var log = new ExceptionLog()
            {
                ExpCode = exceptioCode,
                ExpInfo = "",
                ExpLevel = ErrorLevel.Warnning.ToString(),
                Context = $"{content} {exMsg}",
                AppCode = "FAS",
                CreateTime = DateTime.Now,
                ModuleCode = "ActionRTP",
                ServerIP = GetHostName()
            };

            if (ex != null)
            {
                log.ExpType = ex.GetType().ToString();
                log.ExpStack = ex.StackTrace;
                log.ExpInfo = ex.Message;
                if (ex.InnerException != null)
                {
                    log.InnerExpInfo = ex.InnerException.Message;
                }
            }
            CacheLog(log);
        }
        /// <summary>
        /// 异常日志
        /// </summary>
        /// <param name="metaDataID"></param>
        /// <param name="dataID"></param>
        /// <param name="content"></param>
        /// <param name="exceptioCode"></param>
        /// <param name="ex"></param>
        public void LogError(string metaDataID, string dataID,  string content = null, string exceptioCode = null, Exception ex = null)
        {
            string msgId = RpcContext.Current.RequestID;
            string exMsg = ex != null ? ex.ToString() : string.Empty;
            var log = new ExceptionLog()
            {
                ExpCode = exceptioCode,
                ExpInfo = "",
                ExpLevel = ErrorLevel.Warnning.ToString(),
                Context = $"{content} {exMsg}",
                AppCode = "FAS",
                CreateTime = DateTime.Now,
                ModuleCode = "WF",
                ServerIP = GetHostName(),
                Ext1 = metaDataID,
                Ext2 = dataID,
                Ext3 = msgId,
                ExtColumnNames = $"Ext1=元数据ID,Ext2=数据ID,Ext3=全链路Id"
            };

            if (ex != null)
            {
                log.ExpType = ex.GetType().ToString();
                log.ExpStack = ex.StackTrace;
                log.ExpInfo = ex.Message;
                if (ex.InnerException != null)
                {
                    log.InnerExpInfo = ex.InnerException.Message;
                }
            }
            CacheLog(log);
        }
        /// <summary>
        /// 跟踪日志
        /// </summary>
        /// <param name="content"></param>
        /// <param name="ex">如果需要记录异常信息，建议使用LogError方法</param>
        public void LogTrace(string content, Exception ex = null)
        {

            string exMsg = ex != null ? ex.ToString() : string.Empty;
            var log = new TrackLog()
            {
                Context = $"{content} {exMsg}",
                AppCode = "FAS",
                CreateTime = DateTime.Now,
                ModuleCode = "ActionRTP",
                ServerIP = GetHostName()
            };
            CacheLog(log);
        }
        /// <summary>
        /// 跟踪日志
        /// </summary>
        /// <param name="metaDataID"></param>
        /// <param name="dataID"></param>
        /// <param name="content"></param>
        /// <param name="ex"></param>
        public void LogTrace(string metaDataID, string dataID, string content, Exception ex = null)
        {
            string msgId = RpcContext.Current.RequestID;
            string exMsg = ex != null ? ex.ToString() : string.Empty;
            var log = new TrackLog()
            {
                Context = $"{content} {exMsg}",
                AppCode = "FAS",
                CreateTime = DateTime.Now,
                ModuleCode = "WF",
                ServerIP = GetHostName(),
                Ext1 = metaDataID,
                Ext2 = dataID,
                Ext3 = msgId,
                ExtColumnNames = $"Ext1=元数据ID,Ext2=数据ID,Ext3=全链路Id"
            };
            CacheLog(log);
        }

        private void CacheLog(SysActionLog log)
        {
            sysActionLogs.Add(log);
        }
        private void CacheLog(ExceptionLog log)
        {
            exceptionLogs.Add(log);
        }
        private void CacheLog(TrackLog log)
        {
            trackLogs.Add(log);
        }
        private void StartLogProcess()
        {
            SendSysActionLog();
            SendExceptionLog();
            SendTrackLog();
        }

        private void SendSysActionLog()
        {
            logSendTask = Task.Factory.StartNew(() =>
            {
                //重新初始化一下RequestID，防止日志记录在正常链路中。同时这些日志不需要记录全链路了，因为没有意义，所以初始化RequestID为空
                RpcContext.Current.RequestID = "";
                while (isExist == false)
                {
                    autoResetLog.WaitOne(1000);
                    try
                    {
                        List<ILog> logs = new List<ILog>();
                        ILog log = null;
                        int index = 0;
                        while (sysActionLogs.TryTake(out log))
                        {
                            if (log != null)
                            {
                                logs.Add(log);
                                index++;
                            }
                            if (index >= 100)
                            {
                                var logCT = LogService;
                                if (logCT != null)
                                {
                                    logCT.SendMsgs(logs);
                                    logs.Clear();
                                    index = 0;

                                }
                            }
                        }
                        if (index > 0)
                        {
                            var logC = LogService;
                            if (logC != null)
                            {
                                logC.SendMsgs(logs);
                            }
                        }
                    }
                    catch
                    {
                        // ignored
                    }
                }
            });
        }
        private void SendExceptionLog()
        {
            logSendTask = Task.Factory.StartNew(() =>
            {
                //重新初始化一下RequestID，防止日志记录在正常链路中。同时这些日志不需要记录全链路了，因为没有意义，所以初始化RequestID为空
                RpcContext.Current.RequestID = "";
                while (isExist == false)
                {
                    autoResetLog.WaitOne(1000);
                    try
                    {
                        List<ILog> logs = new List<ILog>();
                        ILog log = null;
                        int index = 0;
                        while (exceptionLogs.TryTake(out log))
                        {
                            if (log != null)
                            {
                                logs.Add(log);
                                index++;
                            }
                            if (index >= 100)
                            {
                                var logCT = LogService;
                                if (logCT != null)
                                {
                                    logCT.SendMsgs(logs);
                                    logs.Clear();
                                    index = 0;

                                }
                            }
                        }
                        if (index > 0)
                        {
                            var logC = LogService;
                            if (logC != null)
                            {
                                logC.SendMsgs(logs);
                            }
                        }
                    }
                    catch
                    {
                        // ignored
                    }
                }
            });
        }
        private void SendTrackLog()
        {
            logSendTask = Task.Factory.StartNew(() =>
            {
                //重新初始化一下RequestID，防止日志记录在正常链路中。同时这些日志不需要记录全链路了，因为没有意义，所以初始化RequestID为空
                RpcContext.Current.RequestID = "";
                while (isExist == false)
                {
                    autoResetLog.WaitOne(1000);
                    try
                    {
                        List<ILog> logs = new List<ILog>();
                        ILog log = null;
                        int index = 0;
                        while (trackLogs.TryTake(out log))
                        {
                            if (log != null)
                            {
                                logs.Add(log);
                                index++;
                            }
                            if (index >= 100)
                            {
                                var logCT = LogService;
                                if (logCT != null)
                                {
                                    logCT.SendMsgs(logs);
                                    logs.Clear();
                                    index = 0;

                                }
                            }
                        }
                        if (index > 0)
                        {
                            var logC = LogService;
                            if (logC != null)
                            {
                                logC.SendMsgs(logs);
                            }
                        }
                    }
                    catch
                    {
                        // ignored
                    }
                }
            });
        }
        public ILogService LogService
        {
            get
            {
                // return HSFService.Proxy<ILogService>();
                return null;
            }
        }
        public void Stop()
        {
            isExist = true;
            autoResetLog.Set();
        }
    }
}
